﻿using System;
using System.ComponentModel;
using System.IO;
using System.Xml;
using Aeon.Emulator.Dos.VirtualFileSystem;

namespace Aeon.Configuration
{
    /// <summary>
    /// Describes the configuration of a folder on the host file system mapped to a virtual drive.
    /// </summary>
    [DriveConfiguration("MappedFolder")]
    public class MappedFolderConfiguration : AeonConfiguration, IDriveConfiguration, IEquatable<MappedFolderConfiguration>
    {
        private bool readOnly;
        private string hostPath;

        /// <summary>
        /// Initializes a new instance of the MappedFolderConfiguration class.
        /// </summary>
        public MappedFolderConfiguration()
        {
            ValidateHostPath();
        }

        /// <summary>
        /// Gets the localized name of the drive configuration type.
        /// </summary>
        public static string Name
        {
            get { return "Folder on the host computer"; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the drive is read-only.
        /// </summary>
        public bool IsReadOnly
        {
            get
            {
                return this.readOnly;
            }
            set
            {
                if(value != this.readOnly)
                {
                    this.readOnly = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("IsReadOnly"));
                }
            }
        }
        /// <summary>
        /// Gets or sets the path on the host file system to map to the virtual drive.
        /// </summary>
        public string HostPath
        {
            get
            {
                return this.hostPath;
            }
            set
            {
                if(value != this.hostPath)
                {
                    this.hostPath = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("HostPath"));
                    ValidateHostPath();
                }
            }
        }

        /// <summary>
        /// Returns a new instance of the emulated drive.
        /// </summary>
        /// <returns>New instance of the emulated drive.</returns>
        public virtual IMappedDrive Create()
        {
            if(this.IsReadOnly)
                return new MappedFolder(this.HostPath);
            else
                return new WritableMappedFolder(this.HostPath);
        }
        /// <summary>
        /// Returns a deep copy of the drive configuration.
        /// </summary>
        /// <returns>Deep copy of the drive configuration.</returns>
        public virtual IDriveConfiguration Clone()
        {
            return new MappedFolderConfiguration()
            {
                IsReadOnly = this.IsReadOnly,
                HostPath = this.HostPath
            };
        }
        /// <summary>
        /// Tests for value equality with another object.
        /// </summary>
        /// <param name="obj">Other object to test for equality.</param>
        /// <returns>Value indicating whether objects are equal.</returns>
        public override bool Equals(object obj)
        {
            return Equals(obj as MappedFolderConfiguration);
        }
        /// <summary>
        /// Tests for value equality with another object.
        /// </summary>
        /// <param name="other">Other object to test for equality.</param>
        /// <returns>Value indicating whether objects are equal.</returns>
        public bool Equals(MappedFolderConfiguration other)
        {
            if(other == null)
                return false;

            return this.IsReadOnly == other.IsReadOnly && this.HostPath == other.HostPath;
        }
        /// <summary>
        /// Returns a hash code for the instance.
        /// </summary>
        /// <returns>Hash code for the instance.</returns>
        public override int GetHashCode()
        {
            var path = this.HostPath;
            if(path != null)
                return path.GetHashCode();
            else
                return 0;
        }
        /// <summary>
        /// Returns the mapped host file system path.
        /// </summary>
        /// <returns>The mapped host file system path.</returns>
        public override string ToString()
        {
            return this.HostPath;
        }

        /// <summary>
        /// Serializes the configuration to an XML element.
        /// </summary>
        /// <param name="xmlWriter">XmlWriter used to serialize the object.</param>
        protected override void Serialize(XmlWriter xmlWriter)
        {
            if(xmlWriter == null)
                throw new ArgumentNullException("xmlWriter");

            xmlWriter.WriteAttributeString("IsReadOnly", this.IsReadOnly.ToString());
            xmlWriter.WriteAttributeString("HostPath", this.HostPath ?? string.Empty);
        }
        /// <summary>
        /// Deserializes the configuration from an XML element.
        /// </summary>
        /// <param name="xmlReader">XmlReader used to deserialize the object.</param>
        protected override void Deserialize(XmlReader xmlReader)
        {
            if(xmlReader == null)
                throw new ArgumentNullException("xmlReader");

            bool readOnly;
            if(!bool.TryParse(xmlReader.GetAttribute("IsReadOnly") ?? string.Empty, out readOnly))
                throw new InvalidDataException();

            this.IsReadOnly = readOnly;
            this.HostPath = xmlReader.GetAttribute("HostPath") ?? string.Empty;
        }

        /// <summary>
        /// Validates the HostPath property value.
        /// </summary>
        private void ValidateHostPath()
        {
            var path = this.HostPath;
            if(string.IsNullOrEmpty(path))
            {
                SetPropertyError("HostPath", "Host file system path is required.");
                return;
            }

            if(path.IndexOfAny(Path.GetInvalidPathChars()) >= 0)
            {
                SetPropertyError("HostPath", "Invalid host file system path.");
                return;
            }

            if(!Directory.Exists(path))
            {
                SetPropertyError("HostPath", "Invalid host file system path.");
                return;
            }

            ClearPropertyError("HostPath");
        }
    }
}
